package com.dirven.game.sprite;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.*;
import com.badlogic.gdx.math.collision.*;
import com.dirven.game.*;
import com.dirven.game.geometry.*;
import com.dirven.game.geometry.Rectangle;

public class Laser extends Sprite {

	private Animation animNozzle;
	private float animStateTime;
	
	Sprite beam;
	float distanceMoved = 0;
	float angle;
	boolean movingRight = true;
	Line beamLine;
	int facingDirection;
	float beamX;
	
	public final static int MOVEMENTTYPE_STILL = 0,
					 		MOVEMENTTYPE_ROTATION = 1,
					 		MOVEMENTTYPE_ANCHOR = 2;
	
	private int laserMovementType = MOVEMENTTYPE_STILL;
	
	// Rotation movemnet variables
	private float startAngle, 
		  		  finishAngle,
		  		  beamLength;
	private boolean reverseDirection,
					reverseDirectionEnabled;
	
	public Laser reflectedLaser; 
	
	private float adjustmentAngle = 0;
	private boolean clockwise;
	
	public Laser(float x, float y, int facingDirection) {
		super(x, y, Sprite.TYPE_OBJECT, "laser", "laser_nozzle_anim.png");

		TextureRegion[] nozzleFrames = TextureRegion.split(getTexture(), 8, 16)[0];
		if(facingDirection == Sprite.DIRECTION_SOUTH) {
			for(int i = 0; i < nozzleFrames.length; i++) {
				TextureRegion frame = nozzleFrames[i];
				frame.flip(false, true);
			}
		}
		animNozzle = new Animation(0.01f, nozzleFrames);
		animStateTime = (float)Math.random();
		
		
		beam = new Sprite(x, y, Sprite.TYPE_OBJECT, "laser", "laser_beam.png");
		
		this.facingDirection = facingDirection;
		
		switch(facingDirection) {
			case Sprite.DIRECTION_NORTH: 
				adjustmentAngle = 0.0f;
				beam.setPosition(beam.getX(), beam.getY()+16);
			break;
			
			case Sprite.DIRECTION_SOUTH: 
				// Flip vertically
				//super.flip(false, true);
				super.rotate(-180.0f);
				adjustmentAngle = -180.0f;
			break;
			
			case Sprite.DIRECTION_EAST: 
				// Rotate 90 degrees
				super.rotate(-90.0f);
				adjustmentAngle = -90.0f;
			break;
			
			case Sprite.DIRECTION_WEST: 
				// Rotate 90 degrees counter clock wise
				super.rotate(90.0f);
				adjustmentAngle = 90.0f;
			break;
		}
		
		//setupBeam((float)DirVenGame.screenWidth/2+50, 50);
		//beamX = (float)DirVenGame.screenWidth/2+50;
		setRotationRange(-20, 20, 500, false, true);
		//setupBeamAngleLength(12, 500);
	}
	
	public void setReflection(float x, float y, float angle) {
		
		if(reflectedLaser == null) {
			reflectedLaser = new Laser(x, y, this.facingDirection);
			reflectedLaser.setupBeamAngleLength(angle, 250);
		} else {
			reflectedLaser.setPosition(x, y);
			reflectedLaser.beam.setPosition(x, y);
			reflectedLaser.setupBeamAngleLength(angle, 250);
		}
	}
	
	public void setRotationRange(float startAngle, float finishAngle, float beamLength, boolean clockwise, boolean reverseDirectionEnabled) {
		laserMovementType = MOVEMENTTYPE_ROTATION;
		this.startAngle = startAngle;
		this.finishAngle = finishAngle;
		this.beamLength = beamLength;
		this.clockwise = clockwise;
		this.reverseDirectionEnabled = reverseDirectionEnabled;
		
		angle = startAngle;
		setupBeamAngleLength(startAngle, beamLength);
	}
	
	public void setupBeamAngleLength(float angle, float length) {
		
		angle += adjustmentAngle;
		
		float startPointX = super.getX(),
			  startPointY = super.getY();
		
		float endPointX = (float) (Math.sin(Math.toRadians(angle)) * length);
		float endPointY = (float) (Math.cos(Math.toRadians(angle)) * length);
		System.out.println("endPointX="+endPointX+", endPointY="+endPointY);
		setupBeam(startPointX+endPointX, startPointY+endPointY);
	}
	
	public void setupBeam(float endPointX, float endPointY) {
		
		// Reset the rotation of the beam
		beam.resetRotation();
		
		float startPointX = super.getX(),
			  startPointY = super.getY();
		// Distance formula
		float length = (float)Math.sqrt( Math.pow((double)(startPointX-endPointX), 2.0) + Math.pow((double)(startPointY-endPointY), 2.0) );
		
		// Resize the beam's length
		beam.setSize(beam.getWidth(), length);
		
		
		if(startPointX > endPointX && startPointY < endPointY) {
			float angle = (float)(Math.atan2(endPointY-startPointY, endPointX-startPointX) * 180 / Math.PI) - 90.0f;
			//The -90.0f is because the angle 0 is the top of the vertical axis.
			
			// Since in Q1, we must rotate in -ve direction.
			beam.rotate(angle);
		} else if(startPointX > endPointX && startPointY > endPointY) {
			float angle = (float)(Math.atan2(endPointY-startPointY, endPointX-startPointX) * 180 / Math.PI) - 90.0f;
			//The -90.0f is because the angle 0 is the top of the vertical axis.
			
			// Since in Q1, we must rotate in -ve direction.
			beam.rotate(angle);
			
		} else if(startPointX < endPointX && startPointY < endPointY) {
			float angle = (float)(Math.atan2(endPointY-startPointY, endPointX-startPointX) * 180 / Math.PI) - 90.0f;
			//The -90.0f is because the angle 0 is the top of the vertical axis.
			
			// Since in Q1, we must rotate in -ve direction.
			beam.rotate(angle);
			
		} else if(startPointX < endPointX && startPointY > endPointY) {
			float angle = (float)(Math.atan2(endPointY-startPointY, endPointX-startPointX) * 180 / Math.PI) - 90.0f;
			//The -90.0f is because the angle 0 is the top of the vertical axis.
			
			// Since in Q1, we must rotate in -ve direction.
			beam.rotate(angle);
			
		} else if(startPointX == endPointX) {
			// Vertical beam. Don't rotate.
			
			if(startPointY > endPointY) {
				beam.rotate(-180);
			}
			
		} else if(startPointY == endPointY) {
			// Horizontal beam. 
			if(startPointX > endPointX) {
				beam.rotate(90);
			} else {
				beam.rotate(-90);
			}
		}
		
		if(beamLine == null) {
			beamLine = new Line(startPointX, startPointY, endPointX, endPointY);
		} else {
			beamLine.setPoints(startPointX, startPointY, endPointX, endPointY);
		}
	}
	
	public void move() {
		/*if(movingRight) {
			//super.shiftPosition(1, 0);
			//beam.shiftPosition(1, 0);
			beamX++;
			distanceMoved++;
			
			if(distanceMoved > 250) {
				distanceMoved = 0;
				movingRight = false;
			}
			
		} else {
			//super.shiftPosition(-1, 0);
			//beam.shiftPosition(-1, 0);
			beamX--;
			distanceMoved++;
			
			if(distanceMoved > 250) {
				distanceMoved = 0;
				movingRight = true;
			}
		}
		setupBeam(beamX, 50);*/
		
		if(laserMovementType == MOVEMENTTYPE_ROTATION) {
			float deltaAngle = 0;
				
			if(clockwise) {
				deltaAngle = -1;
			} else {
				deltaAngle = 1;
			}
			
			if(reverseDirection) {
				deltaAngle *= -1;
			}
			
			angle += deltaAngle;
			System.out.println("angle="+angle + " start="+startAngle+" finish="+finishAngle);
			if(angle >= finishAngle) {
				if(reverseDirectionEnabled) {
					reverseDirection = true;
				} else {
					angle = startAngle;
				}
			}
			if(angle <= startAngle) {
				if(reverseDirectionEnabled) {
					reverseDirection = false;
				} else {
					angle = finishAngle;
				}
			}
			
			/*if( (deltaAngle > 0 && angle > finishAngle) || (deltaAngle < 0 && angle < finishAngle) ) {
				if(reverseDirectionEnabled) {
					reverseDirection = true;
				} else {
					angle = startAngle;
				}
			} else if((deltaAngle > 0 && angle < startAngle) || (deltaAngle < 0 && angle > startAngle)) {
				if(reverseDirectionEnabled) {
					reverseDirection = false;
				} else {
					angle = finishAngle;
				}
			}*/
			setupBeamAngleLength(angle, beamLength);
		}
	}
	
	private boolean withinRange(float val, float min, float max) {
		// min is greater than max, switch them
		if(min > max) {
			float tempMin = max;
			max = min;
			min = tempMin;
		}
		
		if(val >= min && val <= max) {
			return true;
		} else {
			return false;
		}
	}
	
	public float getDistanceToPoint(float x, float y) {
		return (float)( Math.sqrt( Math.pow(x - super.getX(), 2) + Math.pow(y - super.getY(), 2) ) );
	}
	
	public boolean intersectsGridCoord(Coord gridCoord) {
		
		float x1 = gridCoord.x*Grid.cellWidth,
			  y1 = gridCoord.y*Grid.cellHeight,
			  x2 = x1 + Grid.cellWidth,
			  y2 = y1 + Grid.cellHeight;
		
		/*switch(facingDirection) {
			case Sprite.DIRECTION_NORTH: 
				
			break;
			
			case Sprite.DIRECTION_SOUTH: 
				
			break;
			
			case Sprite.DIRECTION_EAST: 
				
			break;
			
			case Sprite.DIRECTION_WEST: 
				
			break;
		}*/
		
		// y value for vertical line is infinity,, so avoid checking the y-value.
		if(!beamLine.isVertical()) {
			
			float yAtX1 = beamLine.getY(x1),
				  yAtX2 = beamLine.getY(x2);
			
			// If line is within the dimensions of the gridcell box.
			if(withinRange( yAtX1, y1, y2) || withinRange( yAtX2, y1, y2)) {
				
				// If the position of the vertices of the box are within the restrictions of the line.
				if(withinRange(x1, beamLine.minX, beamLine.maxX) || withinRange(x2, beamLine.minX, beamLine.maxX)) {

					if(withinRange(y1, beamLine.minY, beamLine.maxY) || withinRange(y2, beamLine.minY, beamLine.maxY)) {
					
						return true;
					}
				}
			}
		}
		// x value for horizontal line  is infinity, so avoid checking the x-value.
		if(!beamLine.isHorizontal()) {
			float xAtY1 = beamLine.getX(y1),
				  xAtY2 = beamLine.getX(y2);
			
			// If line is within the dimensions of the gridcell box.
			if(withinRange( xAtY1, x1, x2) || withinRange( xAtY2, x1, x2)) {
				
				// If the position of the vertices of the box are within the restrictions of the line.
				if(withinRange(y1, beamLine.minY, beamLine.maxY) || withinRange(y2, beamLine.minY, beamLine.maxY)) {
					
					if(withinRange(x1, beamLine.minX, beamLine.maxX) || withinRange(x2, beamLine.minX, beamLine.maxX)) {
						
						return true;
					}
				}
			}
		}
		
		return false;
		
	}

	public void update() {
		move();
		animStateTime += Gdx.graphics.getDeltaTime();
	}
	
	@Override
	public void draw(SpriteBatch batch) {
		//super.draw(batch);
		
		
		TextureRegion frame = animNozzle.getKeyFrame(animStateTime, true);
		batch.draw(frame, super.getX(), super.getY());
		
		beam.draw(batch);
		
		if(reflectedLaser != null) {
			reflectedLaser.beam.draw(batch);
		}
	}
	
	public void finalize() {
		
		try {
			beam.finalize();
			super.finalize();
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
